/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.frontendproject;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.service.MetadataProjectService;
import com.inspur.edp.lcm.metadata.api.service.MetadataService;
import com.inspur.edp.web.common.JITEngineConstants;
import com.inspur.edp.web.common.customexception.WebCustomException;
import com.inspur.edp.web.common.entity.ResultCode;
import com.inspur.edp.web.common.entity.ResultMessage;
import com.inspur.edp.web.common.entity.TerminalType;
import com.inspur.edp.web.common.environment.ExecuteEnvironment;
import com.inspur.edp.web.common.io.FileUtility;
import com.inspur.edp.web.common.logger.WebLogger;
import com.inspur.edp.web.common.metadata.GspProjectUtility;
import com.inspur.edp.web.common.metadata.MetadataGetterParameter;
import com.inspur.edp.web.common.metadata.MetadataTypeEnum;
import com.inspur.edp.web.common.metadata.MetadataUtility;
import com.inspur.edp.web.common.serialize.SerializeUtility;
import com.inspur.edp.web.common.utility.CommandLineUtility;
import com.inspur.edp.web.common.utility.StringUtility;
import com.inspur.edp.web.formmetadata.metadata.FormMetadataContent;
import com.inspur.edp.web.formmetadata.metadata.formdom.FormDOM;
import com.inspur.edp.web.formmetadata.metadataanalysis.CommandsAnalysis;
import com.inspur.edp.web.formmetadata.metadatamanager.CommandServiceManager;
import com.inspur.edp.web.frontendproject.build.FrontendProjectBuild;
import com.inspur.edp.web.frontendproject.customservice.SourceServicePathGenerator;
import com.inspur.edp.web.frontendproject.deploy.FrontendProjectDeployer;
import com.inspur.edp.web.frontendproject.entity.ChosenFormList;
import com.inspur.edp.web.frontendproject.formdynamic.FormDynamicMetadataResolver;
import com.inspur.edp.web.frontendproject.formdynamic.FormDynamicParameterValidator;
import com.inspur.edp.web.frontendproject.formdynamic.FormDynamicSourceCodeGenerate;
import com.inspur.edp.web.frontendproject.generate.FrontendProjectGenerate;
import com.inspur.edp.web.frontendproject.generate.FrontendProjectGenerateForBabel;
import com.inspur.edp.web.frontendproject.webservice.FormDynamicParameter;
import com.inspur.edp.web.jitengine.JITEngineManager;
import com.inspur.edp.web.npmpackage.api.entity.NpmPackageResponse;
import io.iec.edp.caf.businessobject.api.entity.DevBasicBoInfo;
import io.iec.edp.caf.businessobject.api.service.DevBasicInfoService;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import org.apache.commons.lang3.SystemUtils;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 前端工程
 *
 * @author noah
 */
public class FrontendProjectManager {
    private FrontendProjectManager() {

    }

    /**
     * 该工程的调用频率极低 无需单例来占用启动及内存空间，及时释放才是王道
     *
     * @return
     */
    public static FrontendProjectManager getInstance() {
        return new FrontendProjectManager();
    }


    /**
     * 解析表单元数据
     *
     * @param projectPath
     */
    public final void resolveFormMetadatas(String projectPath) {
        FrontendProjectService.getInstance().resolveFormMetadatas(projectPath, ChosenFormList.getNewInstance());
    }

    public final void ResolveAndGenerateFrontendProject(String projectPath) {
        FrontendProjectService.getInstance().resolveFormMetadatas(projectPath, ChosenFormList.getNewInstance());
        FrontendProjectGenerate.generateFrontendProject(projectPath);
    }

    /**
     * 解析、生成、编译前端工程
     *
     * @param projectPath
     * @return
     */
    public final ResultMessage<String> resolveAndGenerateAndBuildFrontendProject(String projectPath) {

        // 编译前操作  判定是否需要执行更新操作
        NpmPackageResponse npmPackageResponse = NpmPackageCheckUpdate.check();
        if (!npmPackageResponse.isSuccess()) {
            return ResultCode.custom(2000, npmPackageResponse.getErrorMessage());
        }

        // 解析元数据
        FrontendProjectService.getInstance().resolveFormMetadatas(projectPath, ChosenFormList.getNewInstance());
        // 生成对应代码
        FrontendProjectGenerate.generateFrontendProject(projectPath);
        // 执行代码编译
        FrontendProjectBuild.buildFrontendProject(projectPath, false);

        return ResultCode.success();
    }


    /**
     * @param projectPath
     * @param buildFormList 参与编译的表单列表
     */
    public final void babelBuildAndDeploy(String projectPath, ChosenFormList buildFormList) {
        FrontendProjectService service = FrontendProjectService.getInstance();
        service.resolveFormMetadatas(projectPath, buildFormList);
        FrontendProjectGenerateForBabel.generate(projectPath);
        FrontendProjectBuild.buildFrontendProjectForBabel(projectPath);
        FrontendProjectDeployer.deployFrontendProjectForBabel(projectPath);
    }

    /**
     * 生成解析表单的依赖项
     *
     * @return
     */
    public final ResultMessage<String> resolveAndGenerateFrontendProjectWithDynamic(FormDynamicParameter dynamicParameter) {
        // 生成前校验  判定是否需要执行更新操作
        // 此参数要求isSingleForm参数必须为true
        FormDynamicParameterValidator.validate(dynamicParameter);

        //由于解析型表单传递的元数据路径包含forms  因此此处重新进行获取真实的元数据路径
        String projectPath = MetadataUtility.getInstance().getMetadataProjectPath(dynamicParameter.getProjectPath());
        dynamicParameter.setProjectPath(projectPath);

        ChosenFormList buildFormList = ChosenFormList.getNewInstance();
        // 依据是工程还是单个表单进行解析表单的生成
        // 如果是单个表单解析 那么输出至独立的位置
        // 如果是工程级别的构造，如果所有表单均是解析类型
        // 如果是工程级别的构造，如果部分表单是解析类型
        if (dynamicParameter.isUseSingleForm()) {
            // 如果单表单
            buildFormList.add(dynamicParameter.getFormCode(), true);
        }

        // 执行元数据解析
        FormDynamicMetadataResolver.resolve(dynamicParameter, buildFormList);


        // 执行代码编译
        if (!dynamicParameter.isUseSingleForm()) {
            // 执行代码生成
            FormDynamicSourceCodeGenerate.generate(dynamicParameter, buildFormList);
            FrontendProjectBuild.buildFrontendProject(dynamicParameter.getProjectPath(), dynamicParameter.isUseSingleForm());
        }

        // 执行代码部署
        // 单个表单的解析     整体工程的解析
        FrontendProjectDeployer.deployFrontendProjectForJieXi(dynamicParameter);
        return ResultCode.success();
    }


    /**
     * 低代码build VUE构件
     * @param path 工程路径
     * @param serviceBasePath 构件所在路径
     */
    public static void buildVueFormTs(String path, String serviceBasePath) {
        MetadataProjectService projectService = SpringBeanUtils.getBean(MetadataProjectService.class);
        String projectPath = projectService.getProjPath(path);
        String projectName = GspProjectUtility.getProjectName(projectPath);
        String distRollupPath = FileUtility.combineOptional(projectPath, "metadata", "src", "vueapp", "dist-rollup", projectName);
        excuteBuildVueFormTs(serviceBasePath, distRollupPath);
    }

    /**
     * 低代码build VUE SCSS
     * @param path 工程路径
     * @param scssBasePath 构件所在路径
     */
    public static void buildVueFormScss(String path, String scssBasePath) {
        MetadataProjectService projectService = SpringBeanUtils.getBean(MetadataProjectService.class);
        String projectPath = projectService.getProjPath(path);
        String projectName = GspProjectUtility.getProjectName(projectPath);
        String distRollupPath = FileUtility.combineOptional(projectPath, "metadata", "src", "vueapp", "dist-rollup", projectName);
        executeBuildVueFormScss(scssBasePath, distRollupPath);
    }


    /**
     * 执行build VUE构件
     * @param serviceBasePath 构件所在路径
     * @param distRollupPath build后js所在路径
     */
    public static void excuteBuildVueFormTs(String serviceBasePath, String distRollupPath) {
        // 获取转换命令路径
        String jsTackPath = FileUtility.getCurrentWorkPath(false);
        String tsToJsCommandPath = FileUtility.combineOptional(jsTackPath, "server", "runtime", "nodejs", "serverscripts", "transformer.cjs");

        // 创建 ObjectMapper 实例
        ObjectMapper mapper = new ObjectMapper();
        // 创建 JsonNodeFactory 实例
        JsonNodeFactory factory = mapper.getNodeFactory();
        // 使用 JsonNodeFactory 构造一个 ArrayNode
        ArrayNode arrayNode = factory.arrayNode();
        List<Path> serviceRelativePath = FileUtility.getRelativePaths(serviceBasePath);
        serviceRelativePath.forEach(relativePath -> {
            ObjectNode node = factory.objectNode();
            node.put("source", FileUtility.combine(serviceBasePath, relativePath.toString()));
            node.put("target", FileUtility.combine(distRollupPath, relativePath.getParent().toString()));
            arrayNode.add(node);
        });
        // 将 ArrayNode 转为字符串
        try {
            if (arrayNode != null && arrayNode.size() > 0) {
                String content = mapper.writeValueAsString(arrayNode);
                WebLogger.Instance.info(JITEngineConstants.TsTransformerInfoFileName + " content : " + content);
                FileUtility.writeFile(FileUtility.combine(serviceBasePath, JITEngineConstants.TsTransformerInfoFileName), mapper.writeValueAsString(arrayNode));
                String distRollupCommand = "node " +
                        tsToJsCommandPath + " " +
                        FileUtility.combine(serviceBasePath, JITEngineConstants.TsTransformerInfoFileName);
                WebLogger.Instance.info("distRollupCommand : " + distRollupCommand);
                CommandLineUtility.runCommand(distRollupCommand);
            }
        } catch (JsonProcessingException e) {
            throw new WebCustomException(e);
        } finally {
            FileUtility.deleteFile(FileUtility.combine(serviceBasePath, JITEngineConstants.TsTransformerInfoFileName));
        }
    }

    /**
     * 执行build VUE SCSS
     * @param scssBasePath 构件所在路径
     * @param distRollupPath build后scss所在路径
     */
    public static void executeBuildVueFormScss(String scssBasePath, String distRollupPath) {
        // 获取转换命令路径,服务器目录
        String cssTackPath = FileUtility.getCurrentWorkPath(false);
        String scssToCssCommandPath = FileUtility.combineOptional(cssTackPath, "server", "runtime", "nodejs", "serverscripts", "sass2css.js");

        // 创建 JsonNodeFactory 实例
        JsonNodeFactory factory = JsonNodeFactory.instance;
        ArrayNode arrayNode = factory.arrayNode();
        List<Path> serviceRelativePath = FileUtility.getRelativePaths(scssBasePath);
        //遍历styles目录下所有的文件（styles/表单1/表单1.scss、styles/表单2/表单2.scss）
        serviceRelativePath.forEach(relativePath -> {
            ObjectNode node = factory.objectNode();
            String relativePathString = relativePath.toString();
            if(relativePathString.endsWith(".scss") ){
                String outRelativePathString = relativePathString.substring(0, relativePathString.length() - 5) + ".css";

                node.put("source", FileUtility.combine(scssBasePath, relativePathString));
                node.put("target", FileUtility.combine(distRollupPath, outRelativePathString));
                arrayNode.add(node);
            }
        });

        if (arrayNode.isEmpty()) {
            return;
        }
        arrayNode.forEach(node -> {
            String inputPath = node.get("source").asText();
            String outPath = node.get("target").asText();
            String distRollupCommand = "node " +
                    scssToCssCommandPath +
                    " -i " + inputPath + " -o " +outPath;
            WebLogger.Instance.info("styleDistRollupCommand : " + distRollupCommand);

            try {
                CommandLineUtility.runCommand(distRollupCommand);
            } catch (Exception e) {
                WebLogger.Instance.error(e, "Failed to execute SCSS to CSS conversion: " + e.getMessage());
            }
        });
    }

    private static void resolveCommandService(FormDOM json, String path, String targetFormServiceBasePath, List<String> serviceFilePathList) {
        if (json != null && json.getModule() != null && json.getModule().getServiceRefs() != null && json.getModule().getServiceRefs().size() > 0) {
            String formCode = json.getModule().getCode();

            String destDirectory = FileUtility.combine(targetFormServiceBasePath, formCode.toLowerCase(), "services");
            String directory = destDirectory.replace("/", FileUtility.DIRECTORY_SEPARATOR_CHAR).replace("\\", FileUtility.DIRECTORY_SEPARATOR_CHAR);


            for (HashMap<String, Object> o : json.getModule().getServiceRefs()) {
                if ("0".equals(o.get("isCommon").toString())) {
//                    String code = o.get("code").toString();
                    String fileName = FileUtility.getFileName(o.get("path").toString());
                    String contents = CommandServiceManager.getTypescriptFileContent(path, "", o, ExecuteEnvironment.Design, false);
                    if (StringUtility.isNullOrEmpty(contents)) {
                        return;
                    }

                    if (!FileUtility.exists(destDirectory)) {
                        FileUtility.createDirectory(destDirectory);
                    }

                    FileUtility.writeFile(directory, fileName.toLowerCase(), contents);
                    serviceFilePathList.add(FileUtility.combine(directory, fileName.toLowerCase()));
                }
            }
        }
    }

}
